<!DOCTYPE html>
<!-- saved from url=(0034)https://imtx.me/archives/1344.html -->
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        
        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
        <title> 通往Django Web Designer之路（二） | I'm TualatriX </title>
        <link rel="apple-touch-icon" sizes="76x76" href="https://imtx.me/static/img/touch-icon-ipad.d5027c9927dc.png">
        <link rel="apple-touch-icon" sizes="120x120" href="https://imtx.me/static/img/touch-icon-iphone-retina.007e7a910a86.png">
        <link rel="apple-touch-icon" sizes="152x152" href="https://imtx.me/static/img/touch-icon-ipad-retina.87ac62f8dd0e.png">
        <link rel="alternate" type="application/rss+xml" title="订阅 TualatriX Feed" href="http://feeds.feedburner.com/tualatrix">
        <link rel="logo" type="image/svg" href="https://imtx.me/static/img/logo.dcb35adf1456.svg">
        <meta name="google-site-verification" content="dQrNwIxyr9oBaxexqgzQKlenOC-d-7VmT_asQEizeJU">
        <link href="./通往Django Web Designer之路（二） _ I&#39;m TualatriX_files/imtx-style.d83b0a68d805.css" rel="stylesheet" type="text/css" media="screen">
        
        <script async="" src="./通往Django Web Designer之路（二） _ I&#39;m TualatriX_files/analytics.js"></script><script>
            (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
                    (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
                m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
            })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');

            ga('create', 'UA-503846-2', 'auto');
            ga('send', 'pageview', {
                page: location.pathname,
                location: location.href,
                title: document.title,
              });
        </script>
    </head>
    <body>
        <header id="header">
        <div class="container yue">
            <a id="brand" href="https://imtx.me/">
                <!--?xml version="1.0" encoding="UTF-8" standalone="no"?-->
                <svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: #FFFFFF;">
                <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
                <g id="touch-icon">
                <path d="M12,22 C17.5228475,22 22,17.5228475 22,12 C22,6.4771525 17.5228475,2 12,2 C6.4771525,2 2,6.4771525 2,12 C2,17.5228475 6.4771525,22 12,22 L12,22 Z" id="Logo" fill="#000000"></path>
                <polygon id="T" fill="#FFFFFF" points="7 7 7 8.5 11.0004883 8.5 11.0004883 19 13 19 13 8.5 17 8.5 17 7"></polygon>
                </g>
                </g>
                </svg>
                <strong>I'm TualatriX</strong>
            </a>
            <nav id="nav">
            <a href="https://imtx.me/" title="Home">Home</a>

<a href="https://imtx.me/about/" title="About">About</a>


            </nav>
        </div>
        </header>
        
<article id="main" role="main" class="hentry yue" itemscope="" itemtype="http://schema.org/Article">
<div class="entry-header">
    <div class="container">
        <h1 class="entry-title" itemprop="name">通往Django Web Designer之路（二）</h1>

        <div class="entry-meta">
            <time class="updated" itemprop="datePublished" datetime="Oct. 10, 2009, 9:52 p.m.">Oct 10, 2009</time>
            
        </div>
    </div>
    <div class="entry-content container" itemprop="articleBody">
        
        
        <p>上篇的标题太YD了，还是改成"通往Django Web Designer之路"吧！</p>
<p>这是该系列的第二篇，教一个无任何Python基础的人，如何成为一个Django Web Designer。</p>
<p>之前，做为一个Dango"全能人员"，我既负责后端，也负责前端。真是累唷！</p>
<p>然而，这样的经验让我领会到，如果只做一个纯Web Designer的话，确实是不需要任何Python基础的。现在，我就通过演示一些简单的例子，让keke成为UTCOM的首席前端架构师！</p>
<p>Django框架的设计是相当灵活的，可以让Developer和Designer相互协调，又不会干扰，也就是可以多人分工开发啦。他写他的前端，我写我的后端，必要时，双方也可以快速地协调起来。</p>
<p>先介绍几个Django前端设计的术语（有些是我自己命名的）：</p>
<p><strong>变量标签：</strong></p>
<p>比如，在app页面，开发者跟设计师商定，将会传变量"date"来显示当前日期。则在HTML模板中，可以这样用（两个大括号）：{{ date }}</p>
<p>不过，作为一个前端设计师，肯定不能这么朴实地调用这个变量吧。实际应用中，可能会是这样：&lt;a href="/path-to-link" class="time"&gt;{{ date }}&lt;/a&gt;，也可能是：&lt;a href="/time/{{ date }}"&gt;Current Date&lt;/a&gt;</p>
<p>取决于变量的类型，有时一个变量有很多方法，强大的很，还可以在变量标签中调用它的方法或属性。&lt;a href="/year/{{ date.year }}"&gt;{{ date.year }}&lt;/a&gt;，这样就动态生成了一个访问年份的链接。</p>
<p><strong>控制标签：</strong></p>
<p>控制标签呢，比"变量标签"更强大了，不仅可以处理变量，而且能处理参数，甚至条件判断，它是以{% XXX %}的形式展开的，而且部分是有配对的。比如我们熟悉的"if ... else"，在UTCOM处理用户登录控制的页面中，有如下的标签：</p>
<pre><code>{% if user.is_authenticated %}  
OOXX  
{% else %}  
XXOO  
{% endif %}
</code></pre>
<p>很明了啦，user这个变量有个is_authenticated的方法，调用它会告诉if是真还是假。如果是已验证的用户，则处理OOXX这段HTML，不然，就处理XXOO这段HTML。实际上都是一样的啦，都是"How To Make Love"，只是次序不同。＝。＝</p>
<p><strong>过滤器（filter）</strong></p>
<p>过滤器呢，则是把变量的数据，过滤成自己想要的数据。你也许也想到了，过滤器要与"变量标签"一块用。</p>
<p>举个简单的例子吧。比如有"repeat"这个过滤器，它可以接受一个参数，表示重复几次。现在，有一个"fuck"的变量，它代表"ooxx"这段字符。如果在HTML中调用它：{{ fuck }}，则页面会显示：ooxx。</p>
<p>现在，让我们运用"repeat"吧！</p>
<p>{{ fuck|repeat:"10" }}，哈哈，結果会是：ooxxooxxooxxooxxooxxooxxooxxooxxooxxooxx。</p>
<p>通过这个生动形象的例子，我相信你明白"过滤器"的威力了吧！</p>
<p>通过开发者开发各种各样的过滤器，前端开发者就能自如地应用他们，使得页面的控制变得十分方便～</p>
<p><strong>模板结构</strong></p>
<p>上面三样，就算是Django前端部分的主要语法了。很简单吧！现在讲些结构方面的知识，以给前端设计师一个大致的概览，即一个Django工程，是如何处理前端的呢？</p>
<p>不再举OOXX的例子了，太YD了，把UTCOM搬出来！</p>
<p>大概介绍下UTCOM的目录结构：</p>
<pre><code>utcom/  
----templates/  
--------base.html  
--------footer.html  
--------index.html  
----static/  
--------js/*.js  
--------css/*.css  
--------img/*.png  
----urls.py  
----settings.py  
----manage.py  
appstore/  
----templates/  
--------appstore/  
------------app_base.html  
------------app_index.html  
------------app_detail.html  
------------index.html  
----templatetags/*.py  
----modles.py  
----views.py  
----urls.py
</code></pre>
<p>这是UTCOM的冰山一角，有HTML，有JS，有CSS，有PNG，还有就是以py为后缀的核心文件。虽然前端设计师不需要了解Python，但是还是大概介绍一下吧～</p>
<p>utcom是整个项目的控制目录，里面有settings.py来控制项目的各种設定, urls.py来控制项目的URL显示，manage.py呢？上篇文章已经讲过了，用它来产生开发用的数据库啊，运行开发实例等等。</p>
<p>appstore呢？这是其中一个子模块的名字（PS：就像Ubuntu App Store改成了App Center一样，我也想把这个应用改成appcenter）。这个模块用于网站的"App"这部分的处理。其中，models.py定义了数据模型部分，views.py和urls.py则相互勾结，定义了前端控制部分。比如：</p>
<p>用户访问了"http://ubuntu-tweak.com/app/ubuntu- tweak/"这个URL，首先会来到utcom/urls.py，这个文件说，这个以"/app/"为前缀的处理，交给"appstore"这个模块去搞！然后就来到"appstore/urls.py"了。</p>
<p>"appstore/urls.py"一看是"http://ubuntu-tweak.com/app/ubuntu- tweak"，哦，是访问具体的应用程序的页面的，那么，就交给views.py里的"app_detail"这个函数去处理吧。现在又来到了"appstore/views.py"中的"app_detail"。</p>
<p>"app_detail"这个函数呢，把跟"ubuntu- tweak"有关的数据都准备好，存在一个变量"app"里，然后说："app_detail.html，快把最终页面呈现出来！"</p>
<p>然后，具有魔力的模板渲染机制就开启了，把app_detail.html里的"{{ app.name }}"之类的标签，都展开成"Ubuntu Tweak"之类的HTML文本，最終一个关于Ubuntu Tweak这个软件的介绍页面，就很自然地呈现在访客面前了。</p>
<p>大概就是这么个过程，很容易理解吧？</p>
<p>让我们再看细一点，app_detail.html里，到底有什么东西呢？是整个页面的HTML吗？不是，只是一小部分而已。其余共用的部分，都是在utcom/templates下。</p>
<p>请看：</p>
<p>{% extends "base.html" %}<br>
{% load i18n utils appi18n %}</p>
<p>{% block title %} {{ app.name }} | Ubuntu Tweak {% endblock %}</p>
<p>{% block content %}<br>
{% appi18n app name summary description %}<br>
&lt;div class="appcontent"&gt;<br>
{% include "appstore/app/app_details.html" %}<br>
&lt;/div&gt;<br>
{% include "appstore/index-bar.html" %}<br>
{% endblock %}</p>
<p>啊，就这么几段？如何变成那么长的一个页面了呢？这里就要说到Django模板机制当中的："继承"和"包含"两个机制了。</p>
<p>为了尽可能的重用代码，django的模板支持"继承"。UTCOM，即使有一万个不同的页面，有两点肯定是相同的，即："页头和页脚"。那么，在utcom/templates目录下，我建立了一个base.html，它只包含了页头和页脚通用部分，中间的区域，留了个{% block content %}{% endblock %}这个占位符，就留给其他模块来填充。</p>
<p>举个最简单的例子吧：</p>
<p>base.html</p>
<pre><code>&lt;html&gt;
&lt;head&gt;&lt;title&gt;{% block title %}base template{% endblock %}&lt;/title&gt;&lt;/head&gt;
&lt;body&gt;This is header
 {% block content %} This is content {% endblock %}
This is footer
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>上面这个base.html，是个标准的HTML，除了奇怪的{% block %}{% endblock %}的控制块。这是干吗呢？这个控制块的意思是，我这块区域，可以被其他页面继承后覆盖。如果没有覆盖的话，就把原文呈现出来。</p>
<p>现在，如果单独呈现这个页面的话，没有什么意外，就是标题为"base template"，内容是：</p>
<p>this is header</p>
<p>this is content</p>
<p>this is footer 的页面（为方便我就不弄&lt;p&gt;了）</p>
<p>现在，弄另一个页面：</p>
<p>app_base.html</p>
<pre><code>{% entends "base.html" %}  
{% block title %}app template{% endblock %}  
 {% block content %} This is app content {% endblock %}
</code></pre>
<p>在上面的代码中，你看到了。{% extends "base.html" %}这个控制語句，即表明了，我要继承"base.html"的这个模板。所以，页头和页脚都不需要在写了。</p>
<p>看到它{% block %} {% endblock %}的内容了吗？正是对应前面base.html里的两块可重写部分。也就是说，app_base.html打算重写这两块区域。</p>
<p>现在，app_base.html呈现出来的，则是标题名为"app template"，正文是：</p>
<p>this is header</p>
<p>this is app content</p>
<p>this is footer</p>
<p>的页面。</p>
<p>这就是继承的原理。</p>
<p>好，看懂以后，再回到之前app_detail.html这个页面。其他都看懂了，中间{% include …… %}这个控制标签在做什么呢？它就是在做"包含"的功能。</p>
<p>在UTCOM中，会有App的詳細资料的显示，无非是这个App的Name啊，当前的Version啊，一句话简介（summary），还有所属的Cageory啊。这块区域是相当可以重用的，比如，在首页列出App，按时间列出App，按分类列出App，我都只需要写一段呈现App的HTML即可。</p>
<p>于是，在app_detail.html，我就直接用： {% include "appstore/app/app_details.html" %}，然后当前页面的变量也都会传过去。具体的细节，都写在"app/app_details.html"里了。</p>
<p><strong>静态素材部分</strong></p>
<p>好，页面的结构也粗粗地讲完了，现在有个大概的感觉，django的页面是如何由一片一片的模板拼起来了吧？</p>
<p>下面，则是另一个部分，也是比较关键的，静态素材部分了。页面只有加上够炫的JS，够酷的PNG，还有够棒的CSS，才会是漂亮的网页喔。</p>
<p>回想一下前面的utcom目录结构，里面有个static目录，还有js，img，css等子目录，所有的静态素材，都是放置在这里的。那么，如何引用呢？不会是"/static/js/ooxx.js"这样吧？可以这样，但是要用更合适的方式。</p>
<p>默认情况下，所有的页面都有一个隐藏变量，叫做"MEDIA_URL"，它是一个包含了全域名的静态目录URL，比如：http://ubuntu- tweak.com/static/，所以在HTML模块里，尽量使用这个变量来代表一个静态路径。</p>
<p>比如在弄CSS样式时，应该这样：</p>
<pre><code>&lt;link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}css/style.css" /&gt;
</code></pre>
<p>这样，以后如果需要变更静态素材的目录时，就可以不用改动了。但是，有时CSS里面也会有需要指定具体路径的，django不能把变量传到style.css，所以只有写死路径了，如：background:url(/static/images/bg.png) top center no-repeat #212121;</p>
<p>这部分没啥特别的吧。</p>
<p>差不多啰，附上两篇补充资料，相信稍做复习以后，你就可以好好地开始hack前端了！</p>
<p><a href="http://docs.djangoproject.com/en/dev/topics/templates/#topics-templates">http://docs.djangoproject.com/en/dev/topics/templates/#topics-templates</a></p>
<p>[http://docs.djangoproject.com/en/dev/ref/templates/builtins/#ref-templates- builtins](http://docs.djangoproject.com/en/dev/topics/templates/#topics-</p>
<p>templates)</p>
<p>当然，UTCOM工程有自己的自定义标签和过滤器，这部分将在未来的文章中介绍。</p>
<p>下篇文章，就简单地介绍下如何用Git吧。</p>
        
        

        <p class="meta"> 
        
        </p><p><strong>&lt;推广&gt;</strong> 本站架设于 <a href="http://www.linode.com/?r=3a7a044d481045ae2ff2f4469357779e2322f5c6" title="Linode">Linode 东京机房</a>，同时使用 <a href="http://referyunti.com/?r=72e027aeac0d9b5d" title="云梯">云梯</a> 进行科学上网</p>
        
        
        <small> » <a href="https://imtx.me/archives/1344.html#comments">You can leave a comment.</a></small>
        
        <p></p>
        
        
        


<h3 id="comments">5 Comments</h3>

<ol class="commentlist">
<li class="comment even thread-even depth-1" id="comment-10114">
<div id="div-comment-10114"><div class="comment-author vcard"><cite><a href="http://www.hicrokee.com/" rel="external nofollow">HicroKee</a></cite></div>
<div class="comment-meta commentmetadata"><a href="https://imtx.me/archives/1344.html#comment-10114">2009 October 11, 20:03</a></div>
<p></p><p>没有高亮，可恶的django template</p><p></p>
<div class="reply"><a rel="nofollow" href="https://imtx.me/archives/1344.html#comment-10114#respond" onclick="return addComment.moveForm(&quot;div-comment-10114&quot;, &quot;10114&quot;, &quot;respond&quot;)">Reply</a></div></div>

<ul class="children">
<li class="comment odd depth-2" id="comment-10116">
<div id="div-comment-10116"><div class="comment-author vcard"><cite><a href="http://imtx.me/" rel="external nofollow">TualatriX</a></cite></div>
<div class="comment-meta commentmetadata"><a href="https://imtx.me/archives/1344.html#comment-10116">2009 October 11, 20:44</a></div>
<p></p><p>哈哈，有高亮啊，我懒得加而已，反而是零散的代码块。<br>请看</p><p></p>
<div class="reply"><a rel="nofollow" href="https://imtx.me/archives/1344.html#comment-10116#respond" onclick="return addComment.moveForm(&quot;div-comment-10116&quot;, &quot;10116&quot;, &quot;respond&quot;)">Reply</a></div></div>

</li>
</ul>
</li>
<li class="comment odd thread-odd depth-1" id="comment-10119">
<div id="div-comment-10119"><div class="comment-author vcard"><cite><a href="http://www.studynote.cn/blog" rel="external nofollow">asins</a></cite></div>
<div class="comment-meta commentmetadata"><a href="https://imtx.me/archives/1344.html#comment-10119">2009 October 11, 22:18</a></div>
<p></p><p>我也不太喜欢django的模板引擎，相比之下web.py的来更直接、简洁</p><p></p>
<div class="reply"><a rel="nofollow" href="https://imtx.me/archives/1344.html#comment-10119#respond" onclick="return addComment.moveForm(&quot;div-comment-10119&quot;, &quot;10119&quot;, &quot;respond&quot;)">Reply</a></div></div>

</li>
<li class="comment even thread-even depth-1" id="comment-10122">
<div id="div-comment-10122"><div class="comment-author vcard"><cite><a href="http://joolix.com/" rel="external nofollow">xiooli</a></cite></div>
<div class="comment-meta commentmetadata"><a href="https://imtx.me/archives/1344.html#comment-10122">2009 October 11, 23:14</a></div>
<p></p><p>长见识了，呵呵</p><p></p>
<div class="reply"><a rel="nofollow" href="https://imtx.me/archives/1344.html#comment-10122#respond" onclick="return addComment.moveForm(&quot;div-comment-10122&quot;, &quot;10122&quot;, &quot;respond&quot;)">Reply</a></div></div>

</li>
<li class="comment odd thread-odd depth-1" id="comment-10268">
<div id="div-comment-10268"><div class="comment-author vcard"><cite><a href="http://whhhy.com/" rel="external nofollow">makestory</a></cite></div>
<div class="comment-meta commentmetadata"><a href="https://imtx.me/archives/1344.html#comment-10268">2009 October 18, 00:15</a></div>
<p></p><p>哇 tx真能写啊<br>django 的模板对程序员来说是不怎么灵活    但是对专业的前端人员还是挺友好的吧（因为简单）<br>p.s. django也可以使用其他模板的</p><p></p>
<div class="reply"><a rel="nofollow" href="https://imtx.me/archives/1344.html#comment-10268#respond" onclick="return addComment.moveForm(&quot;div-comment-10268&quot;, &quot;10268&quot;, &quot;respond&quot;)">Reply</a></div></div>

</li>
</ol>

<div id="respond">
<h3>Leave a Comment</h3>
<div class="cancel-comment-reply">
<small><a rel="nofollow" id="cancel-comment-reply-link" href="https://imtx.me/archives/1344.html#respond" style="display:none;">Click here to cancel reply.</a></small>
</div>
<form style="display: block;" action="https://imtx.me/" method="GET" id="commentform" ext="fuckoffspammer/" onsubmit="this.method=&#39;post&#39;;this.action=&#39;/comment&#39;+[&#39;/&#39;, &#39;1&#39;, 3][0]+this.getAttribute(&#39;ext&#39;)"><input type="hidden" name="csrfmiddlewaretoken" value="D34i2AHfjThoKFpFJy6QjW05BJnArZWn">
    
        <p><input id="id_name" maxlength="50" name="name" type="text">
        <label for="author"><small>Name (required)</small></label></p>
        <div id="result"></div>

        <p><input id="id_email" name="email" type="email">
        <label for="email"><small>Mail (will not be published) (required)</small></label></p>


        <p><input id="id_url" name="url" type="url">
        <label for="url"><small>Website</small></label></p>

    

    <p>
    <textarea cols="40" id="id_content" maxlength="3000" name="content" rows="10"></textarea>
    <input id="id_content_type" name="content_type" type="hidden" value="blog.post">
    <input id="id_object_pk" name="object_pk" type="hidden" value="1344">
    <input id="id_timestamp" name="timestamp" type="hidden" value="1479208020">
    <input id="id_security_hash" maxlength="40" name="security_hash" type="hidden" value="c65139f1462e654506a5b0aef085f639c517b3b9">
    <input id="id_parent_id" name="parent_id" type="hidden">
    </p>

    
    <p><input id="id_mail_notify" name="mail_notify" type="checkbox">
        <label for="url"><small>Email me when someone replied</small></label></p>
    

    <p><input name="submit" type="image" src="./通往Django Web Designer之路（二） _ I&#39;m TualatriX_files/submit.png" id="submit" value="Submit Comment">
    <input type="hidden" name="object_id" value="1344"></p>
</form>
</div>

        
    </div>
</div>

        <footer id="footer">
        <div class="container">
            <p>© Copyright 2007 - 2016 by TualatriX.</p>
        </div>
        </footer>
        <script type="text/javascript" src="./通往Django Web Designer之路（二） _ I&#39;m TualatriX_files/imtx.ba6d3cca696a.js" charset="utf-8"></script>
    

</article></body></html>